var jfPD = (function($, document, window) {
  'use strict';

  var $document = $(document),
      that = {
    init: function(config) {
      that.config = config;
      $document.trigger('jfPD:initEarly', [that.config]);
      $document.ready(function() { $document.trigger('jfPD:init', [that.config]); });
      that.initDevice();
    },
    
    device: { platform: 'other', operatingSystem: 'other' },
    
    initDevice: function() {
      var userAgent = navigator.userAgent;
      if (-1 !== userAgent.indexOf('iPhone')) { 
        that.device.platform = 'iphone';
        that.device.operatingSystem = 'ios';
      }
      else if (-1 !== userAgent.indexOf('iPad')) {
        that.device.platform = 'ipad';
        that.device.operatingSystem = 'ios';
      }
      else if (-1 !== userAgent.indexOf('Android')) {
        that.device.platform = 'android';
        that.device.operatingSystem = 'android';
      }
    },
    
    extensions: {},
    appType: 'web', //may get overwritten with value "native"

    log: function(message) {
      if (typeof window.console !== 'undefined') {
        window.console.log(message);
      }
    },
    
    alert: function(message, title) {
      if (title) {
        message = title+': '+message;
      }
      window.alert(message);
    }

  };
  
  return that;

})(window.jQuery, document, window);


jfPD.controller = (function($, document, window) {
  'use strict';

  var ns = jfPD, //access jfPD namespace through that local variable to allow the variable to be easily renamed later on, if necessary
      $document = $(document),
      $window = $(window),
      that = {}; //will point to jfPD.controller instance and can be used in callback functions

  that.clickEvent = 'vclick'; //jQuery Mobile virtual click event (fires earlier than normal click event)
  
  that.initEarly = function(e, config) {
    $.ajaxSetup({
      cache: true //if ajax caching would be disabled (is disabled by default since jQuery 1.5) then not only the pages would be "un-cacheable" but also any javascripts and stylesheets included in those pages
    });
    
    that.$root = $('html'); //the root element id of the app

    that.deviceSizeS = 1;
    that.deviceSizeM = 2;
    that.deviceSizeL = 3;
    
    $('body').delegate('.jfPD_reload', that.clickEvent, function(e) {
      e.preventDefault();
      e.stopPropagation();
      
      that.reloadPage();
    });

    $window.bind('error', that.onError);
    $document.bind('pagebeforechange', that.onPageBeforeChange);
    $('body').delegate("div:jqmData(role='page'), div:jqmData(role='dialog')", 'pageinit', that.onPageInit);
  };
  
  that.initNetworkStateMessage = function() {
    var $messages = that.$root.find(".jfPD_network_message");
    $messages.find(":jqmData(role='button')").buttonMarkup();
    $messages.find(":jqmData(role='controlgroup')").controlgroup();
    $messages.find('.jfPD_hide, .jfPD_reload').bind(that.clickEvent, function() { that.$root.removeClass('jfPD_network_showMessage'); });

    $document.bind('pageloadfailed', function() { if (that.isOffline()) { that.showNetworkStateMessage(); } });
    $document.bind('online', that.showNetworkStateMessage());
  };
  
  that.showNetworkStateMessage = function() {
    var isOffline = that.isOffline();
    if (isOffline && !that.$root.hasClass('jfPD_network_offline')) {
      that.$root.addClass('jfPD_network_offline').removeClass('jfPD_network_online').addClass('jfPD_network_showMessage');
      that.scrollTo(0);    
    }
    else if (!isOffline && that.$root.hasClass('jfPD_network_offline')) {
      that.$root.addClass('jfPD_network_online').removeClass('jfPD_network_offline').addClass('jfPD_network_showMessage');
      that.scrollTo(0);    
    }
  };
  
  that.isOffline = function() {
    return typeof navigator.onLine === 'undefined' ? false : !navigator.onLine;
  };
  
  that.init = function() {
    that.initNetworkStateMessage();
    setTimeout(function() { that.healCorruptedState(); }, 10000);
  };

  that.getCurrentContainer = function() {
    return $.mobile.activePage;
  };

  that.getCurrentContainerId = function() {
    return that.getCurrentContainer().attr('id');
  };
  
  that.initializePage = function() {
    return $.mobile.initializePage();
  };

  that.reloadPage = function() {
    var $page = that.getCurrentContainer();
    var url = $page.attr('data-url');
    that.showLoading();
    that.refreshPage(url);
  };
  
  that.refreshPages = function() {
    var urls = ns.config.refreshUrls;
    for (var i = 0, count = urls.length; i < count; i++) {
      that.refreshPage(urls[i]);
    }
  };

  that.refreshPage = function(url) {
    var options = {reloadPage: true, showLoadMsg: false};
    url = that.normalizeUri(url);
    
    if ($.mobile.activePage.jqmData('url') === url) {
      $.mobile.changePage(url, options);
    }
    else {
      $.mobile.loadPage(url, options);
    }
  };
  
  that.reloadApp = function() {
    window.stop();
    that.loadPage(ns.config.startUrl);
  };

  that.scrollToElement = function(element) {
    var position = $(element).position();
    that.scrollTo(position.top);
  };
  
  that.scrollTo = function(y) {
    $.mobile.silentScroll(y);
  };

  that.loadPage = function(url, forceRefresh, options) {
    ns.log('loadPage: '+url);
    
    if (!that.urlIsHijackable(url)) {
      window.open(url);
    }

    options = options || {};
    url = that.normalizeUri(url);
    
    if (forceRefresh) {
      options.reloadPage = true;
    }

    $.mobile.changePage(url, options);
  };
  
  that.normalizeUri = function(uri) {
    if (that.urlIsHijackable) {
      uri = that.normalizeDomain(uri);
      uri = that.addUrlAppendix(uri);
    }
    return uri;
  };
  
  that.addUrlAppendix = function(url) {
    if (that.urlIsExtended(url)) {
      return url;
    }

    var fragment = '';
    var hashPosition = url.indexOf('#');
    if (-1 !== hashPosition) {
      fragment = url.substr(hashPosition);
      url = url.substr(0, hashPosition);
    }
    
    var urlAppendix = 'jfPD_device=portable&jfPD_appType='+ns.appType+'&jfPD_size='+that.deviceSize;
    
    url = url+(url.match(/\?/) ? '&' : '?')+urlAppendix;
    
    url += fragment;
    
    return url;
  };

  that.showLoading = function($container) {
    window.setTimeout(function() { $.mobile.showPageLoadingMsg(); }, 0); //without setTimeout this sometimes would not work
  };
  that.hideLoading = function($container) {
    window.setTimeout(function() { $.mobile.hidePageLoadingMsg(); }, 0); //without setTimeout this sometimes would not work
  };

  that.isSameDomain = function(url) {
    if (-1 === url.search(/^http:/) && -1 === url.search(/^https:/)) {
      return true;
    }
    
    var env = ns.config.environment;
    var baseUri = env.baseUri;
    url = that.normalizeDomain(url);

    return $.mobile.path.isSameDomain($.mobile.path.parseUrl(url).protocol+'//'+baseUri, url);
  };
  
   /** normalizeDomain can be overwritten if needed (e.g. if you are working with different releases based on subdomains: 
    *  release1.yourdomain.com, release2.yourdomain.com, www.yourdomain.com etc.)
    **/ 
  that.normalizeDomain = function(url) {
    return url;
  };
  
  that.getSubdomain = function(uri) {
    return uri.substr(0, uri.indexOf('.')).replace(/https?:\/\//, '');
  };
  
  that.urlIsHijackable = function(url) {
    return typeof url !== 'undefined' &&
      url.indexOf('#') !== 0 &&
      !url.match(/^javascript:/) &&
      !url.match(/^mailto:/) &&
      !url.match(/^ftp:/) &&
      that.isSameDomain(url);
  };
  
  that.urlIsExtended = function(url) {
    return -1 !== url.indexOf('jfPD_device=portable');
  };

  that.extendLink = function(e) {
    var $element = $(this);    
    if ($element.attr('data-ajax') === 'false' || $element.attr('rel') === 'external' || ($element.attr('target') && $element.attr('target') !== 'self')) { //could not narrow selection down in jQuery selector because it resulted in an "regular expression is too complex" error in firefox
      return;
    }
    $element.attr('href', that.normalizeUri($element.attr('href')));
  };

  that.healCorruptedState = function(triesCount) {
    triesCount = triesCount || 0;
    var $currentContainer = that.getCurrentContainer();
    
    if (triesCount >= 4) {
      ns.alert('Leider ist ein Fehler aufgetreten. Die Anwendung wird neu gestartet.');
      that.reloadApp();
      setTimeout(function() { that.healCorruptedState(); }, 30000);
      return;
    }
    else if (!$currentContainer || !$currentContainer.length || $currentContainer.text() === 'undefined') {
      triesCount++;
    }
    else {
      triesCount = 0;
    }
    setTimeout(function() { that.healCorruptedState(triesCount); }, 400);
  };
  
  that.onError = function(msg, file, line) {
    if (!ns.config.displayErrors) {
      var $currentContainer = that.getCurrentContainer();
      if (!$currentContainer || !$currentContainer.length || $currentContainer.text() === 'undefined') {
        that.reloadApp();
        ns.alert('Leider ist ein Fehler aufgetreten. Die Anwendung wird neu gestartet.');
        return true;
      }
    }
  };
  
  that.onPageBeforeChange = function(e, data) {
    if (data.options.dataUrl) {
      data.options.dataUrl = that.normalizeUri(data.options.dataUrl);
    }
    else if (typeof data.toPage === 'string') {
      data.toPage = that.normalizeUri(data.toPage);
    }
  };
  
  that.onPageInit = function(e) {
    that.setDeviceSize();  
    that.enhancePage($(this));
  };

  that.loadImages = function($page) {
    var $images = $page.find('img[data-jfPD-src]');
    var appear = !!$.fn.appear;
    $images.each(function(idx, elem) {
      var $elem = $(elem);
      if (appear) {
        if (!parseInt($elem.css('min-height'), 10)) {
          $elem.css('min-height', '1px');
        }
        $elem.appear(function(e) {
          $elem.attr('src', $elem.attr('data-jfPD-src')).removeAttr('data-jfPD-src');
        });
      }
      else {
        $elem.attr('src', $elem.attr('data-jfPD-src')).removeAttr('data-jfPD-src');
      }
    });
  };
  
  that.setDeviceSize = function() {
    var screenWidth = window.innerWidth;
    var screenHeight = window.innerHeight;
    if (Math.max(screenWidth, screenHeight) < 400) {
      that.deviceSize = that.deviceSizeS;
    }
    else if (Math.max(screenWidth, screenHeight) >= 900) {
      that.deviceSize = that.deviceSizeL;
    }
    else {
      that.deviceSize = that.deviceSizeM;
    }
  };
  
  that.enhancePage = function($page) {
    $page.find('a:jqmData(prefetch)').each(that.extendLink);
    setTimeout(function() { that.loadImages($page); }, 30);
    $document.trigger('jfPD:enhancePage', [$page]);
  };

  $document.bind('jfPD:initEarly', that.initEarly);
  $document.bind('jfPD:init', that.init);
  
  return that; 

})(jQuery, document, window);
